home *** CD-ROM | disk | FTP | other *** search
/ NeXT Education Software Sampler 1992 Fall / NeXT Education Software Sampler 1992 Fall.iso / Programming / Source / winterp-1.13 / examples / graphcalc.lsp < prev    next >
Encoding:
Lisp/Scheme  |  1991-10-05  |  44.6 KB  |  1,320 lines

  1. ; -*-Lisp-*-
  2. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  3. ;
  4. ; File:         graphcalc.lsp
  5. ; RCS:          $Header: graphcalc.lsp,v 1.2 91/10/05 16:02:06 mayer Exp $
  6. ; Description:  A calculator with "direct manipulation" graphic display of
  7. ;               previous results. Users may also enter expressions in infix
  8. ;               notation and these will be displayed with disambiguation provided
  9. ;               by precedence rules. To run the caculator, just load this file.
  10. ;
  11. ; NOTE: this file requires version of WINTERP extended for HP's XmGraph widget.
  12. ;
  13. ; Author:       Niels Mayer, HPLabs
  14. ; Created:      Sat Jul  7 13:10:12 1990
  15. ; Modified:     Sat Oct  5 16:01:51 1991 (Niels Mayer) mayer@hplnpm
  16. ; Language:     Lisp
  17. ; Package:      N/A
  18. ; Status:       X11r5 contrib tape release
  19. ;
  20. ; WINTERP Copyright 1989, 1990, 1991 Hewlett-Packard Company (by Niels Mayer).
  21. ; XLISP version 2.1, Copyright (c) 1989, by David Betz.
  22. ;
  23. ; Permission to use, copy, modify, distribute, and sell this software and its
  24. ; documentation for any purpose is hereby granted without fee, provided that
  25. ; the above copyright notice appear in all copies and that both that
  26. ; copyright notice and this permission notice appear in supporting
  27. ; documentation, and that the name of Hewlett-Packard and Niels Mayer not be
  28. ; used in advertising or publicity pertaining to distribution of the software
  29. ; without specific, written prior permission.  Hewlett-Packard and Niels Mayer
  30. ; makes no representations about the suitability of this software for any
  31. ; purpose.  It is provided "as is" without express or implied warranty.
  32. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  33.  
  34. (setq *default_graph_orientation* :horizontal)
  35.  
  36. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  37. ;;
  38. ;; test: (display-equation '( 5 * [6 + 7] * 8 + 9 * [10 + 11] ))
  39. ;; test: (display-equation '( 5 ^ 6 + 6 * 7 + 8 / 9 * [10 + 11] ))
  40. ;; test: (display-equation '( 5 ^ A + (sin 300.0) * B +  / 222.3 * [C + D] ))
  41. ;; test: (display-equation '( x mod 10 * 11 + 12 / 13 / 14 / 15 ))
  42. ;; test: (display-equation '( [ HW1 + HW2 + HW3 + 2 * MT1 + HW4 + HW5 + 2 * MT2 + HW6 + HW7 + 4 * FIN ] / 15 ))
  43. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  44. (defun display-equation (algebraic-expression-list)
  45.   (let ((result-graphnode (display-s-expr (inf-to-pre algebraic-expression-list))))
  46.     ;;(send graph_w :layout) ;;-- uncomment this if setting graph_w's :xmn_auto_layout_mode to NIL...
  47.     (send graph_w :CENTER_AROUND_WIDGET result-graphnode)
  48.     result-graphnode            ;return
  49.     )
  50.   )
  51.  
  52. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  53. ;;
  54. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  55. (defun display-s-expr (s-expr)
  56.   (let (result)
  57.     (case (type-of s-expr)
  58.       (FIXNUM
  59.        (setq result
  60.          (send *calc_terminal_value_widget_class* :new
  61.                (float s-expr)))
  62.        )
  63.       (FLONUM
  64.        (setq result
  65.          (send *calc_terminal_value_widget_class* :new
  66.                s-expr)) 
  67.        )
  68.       (SYMBOL
  69.        (setq result        
  70.          (send *calc_terminal_variable_widget_class* :new
  71.                s-expr))
  72.        )
  73.       (CONS
  74.        (if (eql (type-of (car s-expr)) 'CONS)
  75.            (error (format T "display-s-expr: invalid s-expr -- ~A\n"
  76.                   s-expr)))
  77.        (let ((operator-representor-class (get-operator-class (car s-expr))))
  78.          (if operator-representor-class ;get-operator-class returns NIL if the operator was not defined with make-operator
  79.          (setq result        ;create a non-terminal node, an instance of subclass of *calc_operator_widget_class* (see make-operator)
  80.                (send operator-representor-class :new (cdr s-expr)))
  81.              (setq result        ;if the functor is not a recognized operator, then treat entire sexp as a terminal node.
  82.                (send *calc_terminal_sexp_widget_class* :new s-expr))
  83.          ))
  84.        )
  85.       )
  86.     result
  87.     ))
  88.  
  89. ;==============================================================================
  90. ;====================== conversion from infix to prefix========================
  91. ;==============================================================================
  92. ;;; Path: hplabsz!hplabs!hp-sdd!ucsdhub!sdcsvax!ucsd!ames!haven!umd5!jonnyg
  93. ;;; From: jonnyg@umd5.umd.edu (Jon Greenblatt)
  94. ;;; Newsgroups: comp.lang.lisp,comp.lang.lisp.x
  95. ;;; Subject: Re: Algebraic syntax...
  96. ;;; Keywords: syntax parse
  97. ;;; Message-ID: <4924@umd5.umd.edu>
  98. ;;; Date: 19 May 89 19:40:31 GMT
  99. ;;; References: <4919@umd5.umd.edu>
  100. ;;; Reply-To: jonnyg@umd5.umd.edu (Jon Greenblatt)
  101. ;;; Organization: University of Maryland, College Park
  102. ;;; Lines: 106
  103. ;;; Xref: hplabsz comp.lang.lisp:1655
  104. ;;; 
  105. ;;; 
  106. ;;;      Well I got one reply to my request in the for Algebraic conversion.
  107. ;;;  I took this information and made it a read macro for xlisp. This is
  108. ;;;  realy basic code and I plan to eventialy include a optimizer to pull
  109. ;;;  out constant expressions, reduce the number of function calls, and
  110. ;;;  add support for unary operators. Given this is so simple I though I would
  111. ;;;  post it in this form for right now. If anyone was wondering how to write
  112. ;;;  read macros in Xlisp, here is how its done.
  113. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  114. ;;;
  115. ;;; An infix to prefix converter for algebraic expressions.
  116. ;;; From Winston and Horn, Second Edition, pp 185-189.
  117. ;;;
  118. ;
  119. ;    Adapted as a lisp macro by:
  120. ;        Jonathan Roger Greenblatt (jonnyg@rover.umd.edu)
  121. ;        University of Maryland at College Park
  122. ;
  123. ;
  124. ;    (usage:
  125. ;
  126. ;        [ <expr> <oper> <expr> ( <oper> <expr> ) ... ]
  127. ;
  128. ;    <expr>: a lisp expresion.
  129. ;    <oper>: =,+,-,*,/,mod.**,^
  130. ;
  131. ;    Note: [ and ] are part of the syntax, ( and ) mean this part is
  132. ;                optional.
  133. ;
  134. ;    Examples:
  135. ;
  136. ;        [a = 7 * 5 + 4]
  137. ;        [b = 7 + (sin (float a)) + (float [a / 7]) * [3 + a]]
  138. ;
  139. ;    These are expanded to:
  140. ;
  141. ;        (SETQ A (+ (* 7 5) 4))
  142. ;        (SETQ B (+ (+ 7 (SIN (FLOAT A))) (* (FLOAT (/ A 7)) (+ 3 A))))
  143. ;
  144. ;
  145.  
  146. (defun inf-to-pre (ae)
  147.   (labels
  148.     ((weight (operator)
  149.       (case operator
  150.         (= 0)
  151.         (+ 1)
  152.         (- 1)
  153.         (* 2)
  154.         (/ 2)
  155.         (mod 2)
  156.         (** 3)
  157.         (^ 3)
  158.         (t 4)))
  159.  
  160.     (opcode (operator)
  161.       (case operator
  162.         (= 'setq)
  163.         (+ '+)
  164.         (- '-)
  165.         (* '*)
  166.         (/ '/)
  167.         (mod 'mod)
  168.         (** 'expt)
  169.         (^ 'expt)
  170.         (t (error "invalid operator" operator))))
  171.  
  172.     (inf-aux (ae operators operands)
  173.       (inf-iter (cdr ae)
  174.         operators
  175.         (cons (car ae) operands)))
  176.  
  177.     (inf-iter (ae operators operands)
  178.       (cond ((and (null ae) (null operators))
  179.          (car operands))
  180.         ((and (not (null ae))
  181.               (or (null operators)
  182.               (> (weight (car ae))
  183.                  (weight (car operators)))))
  184.          (inf-aux (cdr ae)
  185.               (cons (car ae) operators)
  186.               operands))
  187.         (t (inf-iter ae
  188.                  (cdr operators)
  189.                  (cons (list (opcode (car operators))
  190.                      (cadr operands)
  191.                      (car operands))
  192.                    (cddr operands)))))))
  193.  
  194.   (if (atom ae)
  195.       ae
  196.       (inf-aux ae nil nil))))
  197.  
  198. (setf (aref *readtable* (char-int #\[))
  199.   (cons :tmacro
  200.     (lambda (f c &aux ex)
  201.         (setf ex nil)
  202.         (do () ((eq (peek-char t f) #\]))
  203.             (setf ex (append ex (cons (read f) nil))))
  204.         (read-char f)
  205.         (cons (inf-to-pre ex) nil))))
  206.  
  207. (setf (aref *readtable* (char-int #\]))
  208.   (cons :tmacro
  209.     (lambda (f c)
  210.         (error "misplaced right bracket"))))
  211.  
  212.  
  213. ;==============================================================================
  214. ;======================= *calc_operator_widget_class* =========================
  215. ;==============================================================================
  216.  
  217. ;; the metaclass for nonterminal nodes corresponding to arithmetic
  218. ;; operators.
  219. (setq *calc_operator_widget_class*
  220.       (send Class :new
  221.         '(operator-name
  222.           child-list
  223.           )
  224.         '()
  225.         XM_PUSH_BUTTON_WIDGET_CLASS))
  226.  
  227. ;; override XM_PUSH_BUTTON_WIDGET_CLASS instance initializer
  228. (send *calc_operator_widget_class* :answer :isnew '(args &rest widget_args)
  229.       '(
  230.     (setq child-list NIL)
  231.     (apply 'send-super
  232.            `(:isnew ,@widget_args
  233.             :XMN_FOREGROUND        "Blue"
  234.             :XMN_BACKGROUND        "LightGrey"
  235.             ))
  236.     (send-super :set_callback :XMN_ACTIVATE_CALLBACK '() ;this method should really be part of a metaclass on all objects in the graphcalc graph_w
  237.             `(
  238.               (send *calc_display* :set_display_value_from_graphnode ,self)
  239.               ))
  240.     (do*
  241.      ((arg-list args (cdr arg-list))
  242.       child-widget
  243.       )
  244.      ((null arg-list)
  245.       )
  246.      (setq child-widget
  247.            (display-s-expr (car arg-list)))
  248.      (setq child-list (append child-list (list child-widget)))
  249.  
  250.      (send XM_ARC_WIDGET_CLASS :new :managed
  251.            "" graph_w
  252.            :XMN_TO self
  253.            :XMN_FROM child-widget
  254.            )
  255.      )
  256.     self                ;return self
  257.     ))
  258.  
  259. ;; add individual argument widgets (operands for operator-name)
  260. (send *calc_operator_widget_class* :answer :add_arg '(child-widget)
  261.       '(
  262.     (setq child-list (cons child-widget child-list))
  263.     (send XM_ARC_WIDGET_CLASS :new :managed
  264.           "" graph_w
  265.           :XMN_TO self
  266.           :XMN_FROM child-widget
  267.           )
  268.     self                ;return value
  269.     ))
  270.  
  271. ;; retrieve the value of applying operator on it's children (recursive)
  272. (send *calc_operator_widget_class* :answer :get_value '()
  273.       '(
  274.     (let ((arg-list NIL))
  275.       (do
  276.        ((children child-list (cdr children)))
  277.        ((null children))
  278.        (setq arg-list
  279.          (append arg-list (list (send (car children) :get_value))))
  280.        )
  281.       (float (apply operator-name arg-list)) ;return value
  282.       )
  283.     ))
  284.  
  285. ;; throw away :reorient message
  286. (send *calc_operator_widget_class* :answer :reorient '()
  287.       '(
  288.     self
  289.     ))
  290.  
  291. ;;; not needed - handled by default WIDGET_CLASS :destroy
  292. ;;;;;;recursively destroy all the children of an operator
  293. ;;;(send *calc_operator_widget_class* :answer :destroy '()
  294. ;;;      '(
  295. ;;;    (do
  296. ;;;     ((children child-list (cdr children)))
  297. ;;;     ((null children))
  298. ;;;     (send (car children) :destroy)
  299. ;;;     )
  300. ;;;    (send-super :destroy)
  301. ;;;    ))
  302.  
  303. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  304. ;; Public interface to *calc_operator_widget_class* metaclass
  305. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  306. (let ((operators-alist NIL))
  307.  
  308.   ;; create a subclass of *calc_operator_widget_class*. for each different operator
  309.   ;; we can fetch the subclass via (get-operator-class <operator-symbol>).
  310.   (defun make-operator (operator-symbol)
  311.     (let ((operator-class        ;make a subclass of *calc_operator_widget_class*
  312.        (send Class :new
  313.          '()
  314.          '()            ;no class variables for subclass
  315.          *calc_operator_widget_class*)))
  316.  
  317.       ;; override widget's instance initializer
  318.       (send operator-class :answer :isnew '(args &rest widget-args)
  319.         '(
  320.           (setq operator-name operator-symbol)
  321.           (apply 'send-super
  322.              `(:isnew ,args :managed ,graph_w
  323.                   ,@widget-args
  324.                   :XMN_LABEL_STRING ,(symbol-name operator-symbol)
  325.                   ))
  326.           self            ;return self
  327.           ))
  328.     
  329.       (setq operators-alist (cons (cons operator-symbol operator-class) operators-alist))
  330.       ))
  331.  
  332.   (make-operator '/)
  333.   (make-operator '*)
  334.   (make-operator '-)
  335.   (make-operator '+)
  336.   (make-operator 'mod)
  337.   (make-operator 'expt)
  338.                     ; (make-operator 'setq)
  339.   ;;
  340.   ;; Fetch the operator class created by make-operator
  341.   ;;
  342.   (defun get-operator-class (operator-symbol)
  343.     (let ((alist-elt (assoc operator-symbol operators-alist)))
  344.       (if alist-elt
  345.       (cdr alist-elt)
  346.     NIL)
  347.       ))
  348.   )                    ;end let
  349.  
  350. ; the rem operator is unsuitable for direct use as mod because it insists on FIXNUMS...
  351. (defun mod (x y)
  352.   (setq x (truncate x))
  353.   (setq y (truncate y))
  354.   (rem x y)
  355.   )
  356.  
  357. ;==============================================================================
  358. ;===================== *calc_terminal_value_widget_class* =====================
  359. ;==============================================================================
  360.  
  361. ;; The class of value-bearing terminal nodes
  362. (setq *calc_terminal_value_widget_class*
  363.       (send Class :new
  364.         '(value
  365.           )
  366.         '()
  367.         XM_PUSH_BUTTON_WIDGET_CLASS))
  368.  
  369. ;; override XM_LABEL_WIDGET_CLASS instance initializer
  370. (send *calc_terminal_value_widget_class* :answer :isnew '(value-flonum &rest widget_args)
  371.       '(
  372.     (setq value value-flonum)
  373.     (apply 'send-super
  374.            `(:isnew :managed ,graph_w
  375.             ,@widget_args
  376.             :XMN_LABEL_STRING       ,(format nil "~A" value-flonum)
  377.             :XMN_FOREGROUND        "White"
  378.             :XMN_BACKGROUND        "Black"
  379.             ))
  380.     (send-super :set_callback :XMN_ACTIVATE_CALLBACK '() ;this method should really be part of a metaclass on all objects in the graphcalc graph_w
  381.             `(
  382.               (send *calc_display* :set_display_value_from_graphnode ,self)
  383.               ))
  384.     self                ;return self
  385.     ))
  386.  
  387. (send *calc_terminal_value_widget_class* :answer :get_value '()
  388.       '(
  389.     (float value)                ;return value
  390.     ))
  391.  
  392. ;;; throw away :reorient message
  393. (send *calc_terminal_value_widget_class* :answer :reorient '()
  394.       '(
  395.     self
  396.     ))
  397.  
  398. ;;; message :DESTROY handled by WIDGET_CLASS
  399. ;;;(send *calc_terminal_value_widget_class* :answer :destroy '()
  400. ;;;      '(
  401. ;;;    ))
  402.  
  403.  
  404.  
  405. ;==============================================================================
  406. ;=================== *calc_terminal_variable_widget_class* ====================
  407. ;==============================================================================
  408.  
  409. ;; The class of value-bearing terminal nodes designated as variables.
  410. ;; These are represented as a horizontal row/column widget containing
  411. ;; a label widget (the variable) and a single line texteditor widget
  412. ;; containing the current value of the variable
  413. (setq *calc_terminal_variable_widget_class*
  414.       (send Class :new
  415.         '(
  416.           variable-name        ;SYMBOL
  417.           name-pushbutton-widget    ;WIDGETOBJ
  418.           value-input-editor-widget    ;WIDGETOBJ or NIL if not created
  419.           value-input-scale-widget    ;WIDGETOBJ or NIL if not yet created.
  420.           )
  421.         '()
  422.         XM_ROW_COLUMN_WIDGET_CLASS))
  423.  
  424. ;; override XM_LABEL_WIDGET_CLASS instance initializer
  425. (send *calc_terminal_variable_widget_class* :answer :isnew '(variable-symbol &rest widget-args)
  426.       '(
  427.     (setq variable-name variable-symbol)
  428.     (apply 'send-super
  429.            `(:isnew :managed "terminal-variable-rowcol" ,graph_w
  430.             ,@widget-args
  431.             :XMN_BORDER_WIDTH    2
  432.             :XMN_ORIENTATION    ,*default_graph_orientation*
  433.             :XMN_PACKING        :no_packing
  434.             :XMN_ADJUST_LAST    t
  435.             :XMN_ENTRY_ALIGNMENT    :alignment_center
  436.             :xmn_resize_height      t
  437.             :xmn_resize_width    t
  438.             ))
  439.     (setq name-pushbutton-widget
  440.           (send XM_PUSH_BUTTON_WIDGET_CLASS :new :managed "terminal-variable-name" self
  441.             :XMN_LABEL_STRING (symbol-name variable-symbol)
  442.             ))
  443.     (setq value-input-editor-widget NIL)
  444.     (setq value-input-scale-widget 
  445.           (send XM_SCALE_WIDGET_CLASS :new :managed "terminal-variable-scale" self
  446.             :XMN_ORIENTATION        *default_graph_orientation*
  447.             :XMN_PROCESSING_DIRECTION (if (eq *default_graph_orientation* :horizontal)
  448.                           :MAX_ON_RIGHT 
  449.                         :MAX_ON_TOP)
  450.             :XMN_SENSITIVE        t
  451.             :XMN_SHOW_VALUE        t
  452.             :XMN_MINIMUM        -100
  453.             :XMN_MAXIMUM        +100
  454.             :XMN_VALUE            0
  455.             ))
  456.  
  457.     ;; left mouse button on the pushbutton gets value
  458.     (send name-pushbutton-widget :set_callback :XMN_ACTIVATE_CALLBACK '() ;this method should really be part of a metaclass on all objects in the graphcalc graph_w
  459.           `(
  460.         (send *calc_display* :set_display_value_from_graphnode ,self)
  461.         ))
  462.     
  463.     ;; middle mouse button on pushbutton toggles between slider and editor 
  464.     (send name-pushbutton-widget :set_event_handler BUTTON_PRESS_MASK
  465.           '(EVHANDLER_BUTTON)    ;gets bound to button of event
  466.           `(
  467.         (if (= EVHANDLER_BUTTON 2) ;middle button
  468.             (progn
  469.               (send ,self :set_values ;special hack to work around resizing bug (comment-out to see problem)
  470.                 :xmn_resize_height nil
  471.                 :xmn_resize_width nil
  472.                 )
  473.               (cond
  474.                ((null value-input-scale-widget)
  475.             (send value-input-editor-widget :destroy) ;NOTE: ideally, i'd unmanage/manage rather than destroy/create, but this works around Motif 1.0 resize bugs
  476.             (setq value-input-editor-widget nil)
  477.             (setq value-input-scale-widget 
  478.                   (send XM_SCALE_WIDGET_CLASS :new :managed "terminal-variable-scale" ,self
  479.                     :XMN_ORIENTATION        *default_graph_orientation*
  480.                     :XMN_PROCESSING_DIRECTION (if (eq *default_graph_orientation* :horizontal)
  481.                                   :MAX_ON_RIGHT 
  482.                                 :MAX_ON_TOP)
  483.                     :XMN_SENSITIVE        t
  484.                     :XMN_SHOW_VALUE        t
  485.                     :XMN_MINIMUM        -100
  486.                     :XMN_MAXIMUM        +100
  487.                     :XMN_VALUE        0
  488.                     ))
  489.             )
  490.                ((null value-input-editor-widget)
  491.             (send value-input-scale-widget :destroy) ;NOTE: ideally, i'd unmanage/manage rather than destroy/create, but this works around Motif 1.0 resize bugs
  492.             (setq value-input-scale-widget nil)
  493.             (setq value-input-editor-widget
  494.                   (send XM_TEXT_WIDGET_CLASS :new :managed "terminal-variable-editor" ,self
  495.                     :XMN_STRING        "0"
  496.                     :XMN_EDIT_MODE        :single_line_edit
  497.                     :XMN_AUTO_SHOW_CURSOR_POSITION t
  498.                     :XMN_EDITABLE        t
  499.                     :XMN_FOREGROUND        "Black"
  500.                     :XMN_BACKGROUND        "LightGrey"
  501.                     ))
  502.             )
  503.                )
  504.               (send ,self :set_values ;special hack to work around resizing bug (comment-out to see problem)
  505.                 :xmn_resize_height t
  506.                 :xmn_resize_width t
  507.                 )
  508.               ))
  509.         self            ;return self
  510.         ))
  511.     ))
  512.  
  513. (send *calc_terminal_variable_widget_class* :answer :get_value '()
  514.       '(
  515.     (cond
  516.      (value-input-editor-widget
  517.       (float (read (make-string-input-stream (send value-input-editor-widget :get_string)))) ;return value
  518.       )
  519.      (value-input-scale-widget
  520.       (float (send value-input-scale-widget :get_value)) ;return value
  521.       )
  522.      )))
  523.  
  524. (send *calc_terminal_variable_widget_class* :answer :reorient '()
  525.       '(
  526.     (send-super :set_values        ;special hack to work around resizing bug (comment-out to see problem)
  527.           :xmn_resize_height nil
  528.           :xmn_resize_width nil
  529.           )
  530.     (send-super :set_values        ;special hack to work around resizing bug (comment-out to see problem)
  531.           :XMN_ORIENTATION    *default_graph_orientation*
  532.           )
  533.     (if value-input-scale-widget    
  534.         (let ((value (send value-input-scale-widget :get_value)))
  535.           (send value-input-scale-widget :destroy) ;NOTE: ideally, i'd unmanage/manage rather than destroy/create, but this works around Motif 1.0 resize bugs
  536.           (setq value-input-scale-widget 
  537.             (send XM_SCALE_WIDGET_CLASS :new :managed "terminal-variable-scale" self
  538.               :XMN_ORIENTATION *default_graph_orientation*
  539.               :XMN_PROCESSING_DIRECTION (if (eq *default_graph_orientation* :horizontal)
  540.                             :MAX_ON_RIGHT 
  541.                               :MAX_ON_TOP)
  542.               :XMN_SENSITIVE t
  543.               :XMN_SHOW_VALUE t
  544.               :XMN_MINIMUM -100
  545.               :XMN_MAXIMUM +100
  546.               :XMN_VALUE   value
  547.               ))
  548.           ))
  549.     (if value-input-editor-widget
  550.         (progn (send value-input-editor-widget :unmanage) (send value-input-editor-widget :manage)))
  551.  
  552.     (send-super :set_values
  553.           :xmn_resize_height t
  554.           :xmn_resize_width t
  555.           )
  556.  
  557.     self                ;return value
  558.     ))
  559.  
  560. (send *calc_terminal_variable_widget_class* :answer :destroy '()
  561.       '(
  562.     (send name-pushbutton-widget :destroy)
  563.     (if value-input-editor-widget
  564.         (send value-input-editor-widget :destroy))
  565.     (if value-input-scale-widget
  566.         (send value-input-scale-widget :destroy))
  567.     (send-super :destroy)
  568.     ))
  569.  
  570. ;==============================================================================
  571. ;===================== *calc_terminal_sexp_widget_class* ======================
  572. ;==============================================================================
  573.  
  574. ;; The class of value-bearing terminal nodes designated as lisp s-exprs.
  575. ;; These are represented as a label widget containing the sexpr.
  576. (setq *calc_terminal_sexp_widget_class*
  577.       (send Class :new
  578.         '(s-expression
  579.           )
  580.         '()
  581.         XM_PUSH_BUTTON_WIDGET_CLASS))
  582.  
  583. ;; override XM_PUSH_BUTTON_WIDGET_CLASS instance initializer
  584. (send *calc_terminal_sexp_widget_class* :answer :isnew '(sexpr &rest widget_args)
  585.       '(
  586.     (setq s-expression sexpr)
  587.     (apply 'send-super
  588.            `(:isnew :managed ,graph_w
  589.             ,@widget_args
  590.             :XMN_LABEL_STRING       ,(format nil "~A" sexpr)
  591.             :XMN_FOREGROUND        "Black"
  592.             :XMN_BACKGROUND        "White"
  593.             ))
  594.     (send-super :set_callback :XMN_ACTIVATE_CALLBACK '() ;this method should really be part of a metaclass on all objects in the graphcalc graph_w
  595.             `(
  596.               (send *calc_display* :set_display_value_from_graphnode ,self)
  597.               ))
  598.     self                ;return self
  599.     ))
  600.  
  601. (send *calc_terminal_sexp_widget_class* :answer :get_value '()
  602.       '(
  603.     (float (eval s-expression))    ;return value
  604.     ))
  605.  
  606. ;; throw away :reorient message
  607. (send *calc_terminal_sexp_widget_class* :answer :reorient '()
  608.       '(
  609.     self
  610.     ))
  611.  
  612. ;;; message :DESTROY handled by WIDGET_CLASS
  613. ;;;(send *calc_terminal_sexp_widget_class* :answer :destroy '()
  614. ;;;      '(
  615. ;;;    ))
  616.  
  617.  
  618. ;==============================================================================
  619. ;=========================== *calc_display_class* =============================
  620. ;==============================================================================
  621.  
  622. ;; make a subclass of XM_TEXT_WIDGET_CLASS
  623. (setq *calc_display_class*
  624.       (send Class :new
  625.         '(
  626.           accumulator-value-widget
  627.           display-value-widget
  628.           begin-entry-p
  629.           prev-operator-symbol
  630.           modify-verify-callback-enabled
  631.           )
  632.         '()                ;no class variables for subclass
  633.         XM_TEXT_WIDGET_CLASS)) 
  634.  
  635. ;; override XM_TEXT_WIDGET_CLASS's instance initializer
  636. (send *calc_display_class* :answer :isnew '(&rest args)
  637.       '(
  638.     (setq accumulator-value-widget nil)
  639.     (setq display-value-widget nil)
  640.     (setq begin-entry-p nil)
  641.     (setq prev-operator-symbol nil)
  642.     (apply 'send-super
  643.            `(:isnew :managed :scrolled ,@args
  644.             :XMN_STRING        ""
  645.             :XMN_EDIT_MODE        :single_line_edit
  646.             :XMN_AUTO_SHOW_CURSOR_POSITION t
  647.             :XMN_CURSOR_POSITION    0
  648.             :XMN_EDITABLE        t
  649.             :XMN_FOREGROUND        "Black"
  650.             :XMN_BACKGROUND        "LightGrey"
  651.             ))
  652.     ;; set the colors of the scrollbar -- note Motif's lamo use of hidden
  653.     ;; scroller parent when dealing with a scrolled edit widget (grrr).
  654.     (send
  655.      (car (send (send-super :parent) :get_values :XMN_HORIZONTAL_SCROLL_BAR nil))
  656.      :set_values            ;the scrolled window
  657.      :XMN_HEIGHT     12
  658.      :XMN_FOREGROUND "lightgrey"
  659.      :XMN_BACKGROUND "dimgrey"
  660.      )
  661.  
  662.     ;;note XmText BUG -- causes strange result if pasting into display while this callback is enabled.
  663.     (send-super :set_callback :XMN_MODIFY_VERIFY_CALLBACK '()
  664.             `(
  665.               (if modify-verify-callback-enabled
  666.               (cond
  667.                (begin-entry-p
  668.                 (setq display-value-widget nil)
  669.                 (setq modify-verify-callback-enabled nil) ; :set_string will cause modify verify callback, so disable infinite recursion
  670.                 (send ,self :set_string "")    
  671.                 ;;text entry that caused callback will be input via text widget after clearing...
  672.                 ;;NOTE that XmText is bug-laden and doesn't work right if input is from
  673.                 ;;pasting, infact, it seems to cause a garbled display and possible memory
  674.                 ;;corruption. I'd normally do all this myself, except that setting CALLBACK_DOIT
  675.                 ;;to NIL and doing (send ,self :set_string CALLBACK_TEXT) causes a beep. XmText is a piece of shit.
  676.                 ;;WORKAROUND is to enter a single char (' ') and then paste....
  677.                 (setq modify-verify-callback-enabled t) ; resume handling modify verify callback
  678.                 (setq begin-entry-p nil)
  679.                 ))
  680.             ))
  681.             )
  682.  
  683.     ;; this callback fires when <return> is entered into the text editor widget (single-line version only)
  684.     (send-super :set_callback :XMN_ACTIVATE_CALLBACK '()
  685.           `(
  686.         (send ,self :set_display_value_from_graphnode 
  687.               (display-equation    ;returns a graphnode widget containing expression in the text display
  688.                (read
  689.             (make-string-input-stream
  690.              (strcat "( " (send-super :get_string) " )")))))
  691.         ))
  692.     self                ;return self
  693.     ))
  694.  
  695. ;;
  696. ;; this gets called when a graph node gets clicked, it displays the value
  697. ;; of the clicked graph node and sets that as a possible operand for other
  698. ;; operators
  699. (send *calc_display_class* :answer :set_display_value_from_graphnode '(value-widget)
  700.       '(
  701.     (setq display-value-widget value-widget)
  702.     (setq modify-verify-callback-enabled nil)
  703.     (send-super :set_string (format nil "~A" (send value-widget :get_value)))
  704.     (setq modify-verify-callback-enabled t)
  705.     (setq begin-entry-p t)
  706.     )
  707.       )
  708.  
  709. (send *calc_display_class* :answer :exec_binary_operator '(operator-symbol)
  710.       '(
  711.     ;; if display-value-widget is non-null, then a result has been set by clicking an operator node in the graph widget;
  712.     ;; on first entering an expression in the display, the :XMN_MODIFY_VERIFY_CALLBACK fires which clears the disp and
  713.     ;; sets display-value-widget to NIL. when display-value-widget is NIL we convert the expression in the display into a sexp and graph it.
  714.     (if (null display-value-widget)
  715.         (setq display-value-widget
  716.           (display-equation (read (make-string-input-stream (strcat "( " (send-super :get_string) " )"))))))
  717.     ;; display-value-widget is now guaranteed to hold widget assoc'd with displayed value
  718.  
  719.  
  720.     (cond
  721.      ;; if there is a prev operator, then we want to create a new node corresponding to prev-op-symbol
  722.      ;; whose args are the current value of the accumulator and the current display.
  723.      ;; if the accumilator is NIL, the we don't pass that arg to widget.
  724.      (prev-operator-symbol
  725.       (let ((operator-representor-class (get-operator-class prev-operator-symbol))
  726.         w)
  727.         (cond
  728.          (operator-representor-class ;get-operator-class returns NIL if the operator was not defined with make-operator
  729.           (setq w (send operator-representor-class :new NIL))
  730.           (send w :add_arg display-value-widget)
  731.           (setq display-value-widget w)
  732.           (if accumulator-value-widget
  733.           (send display-value-widget :add_arg accumulator-value-widget))
  734.           (setq modify-verify-callback-enabled nil)
  735.           (send-super :set_string (format nil "~A" (send display-value-widget :get_value)))    ;display the result
  736.           (setq modify-verify-callback-enabled t)
  737.           (setq accumulator-value-widget display-value-widget)
  738.           )))
  739.       )
  740.      ;; else there was no prev operator, meaning last operation was an ==
  741.      ;; just display the value, and save it in the accumulator for next time
  742.      (t
  743.       (setq modify-verify-callback-enabled nil)
  744.       (send-super :set_string (format nil "~A" (send display-value-widget :get_value)))
  745.       (setq modify-verify-callback-enabled t)
  746.       (setq accumulator-value-widget display-value-widget)    
  747.       ))
  748.  
  749.     (setq begin-entry-p t)
  750.     (setq prev-operator-symbol operator-symbol)
  751.     ))
  752.  
  753. (send *calc_display_class* :answer :insert_string '(str)
  754.       '(
  755.     (let ((pos (send-super :GET_INSERTION_POSITION)))
  756.       (send-super :replace pos pos str)
  757.       )
  758.     ))
  759.  
  760. (send *calc_display_class* :answer :change_sign '()
  761.       '(
  762.     (setq modify-verify-callback-enabled nil)
  763.     (send-super :set_string (format nil "~A" (- (float (read (make-string-input-stream (send-super :get_string)))))))
  764.     (setq modify-verify-callback-enabled t)
  765.     ))
  766.  
  767. (send *calc_display_class* :answer :backspace '()
  768.       '(
  769.     (let ((pos (send-super :GET_INSERTION_POSITION)))
  770.       (cond
  771.        ((= pos 0)
  772.         (format T "\007\n")        ;beep
  773.         )
  774.        (t
  775.         (setq modify-verify-callback-enabled nil)
  776.         (send-super :replace (1- pos) pos "")
  777.         (setq modify-verify-callback-enabled t)
  778.         ))
  779.       )))
  780.  
  781. (send *calc_display_class* :answer :delete '()
  782.       '(
  783.     (let ((pos (send-super :GET_INSERTION_POSITION)))
  784.       (cond
  785.        ((= pos (length (send-super :get_string)))
  786.         (format T "\007\n")        ;beep
  787.         )
  788.        (t
  789.         (setq modify-verify-callback-enabled nil)
  790.         (send-super :replace pos (1+ pos) "")
  791.         (setq modify-verify-callback-enabled t)
  792.         ))
  793.       )))
  794.  
  795. (send *calc_display_class* :answer :clear '()
  796.       '(
  797.     (setq display-value-widget nil)
  798.     (setq modify-verify-callback-enabled nil)
  799.     (send-super :set_string "")
  800.     (setq modify-verify-callback-enabled t)
  801.     ))
  802.  
  803. (send *calc_display_class* :answer :forward-char '()
  804.       '(
  805.     (let ((pos (send-super :GET_INSERTION_POSITION)))
  806.       (if (< pos (length (send-super :get_string)))
  807.           (send-super :set_insertion_position (1+ pos))
  808.         (format T "\007\n")
  809.         ))))
  810.  
  811. (send *calc_display_class* :answer :backward-char '()
  812.       '(
  813.     (let ((pos (send-super :GET_INSERTION_POSITION)))
  814.       (if (> pos 0)
  815.           (send-super :set_insertion_position (1- pos))
  816.         (format T "\007\n")
  817.         ))))
  818.  
  819.  
  820. ;==============================================================================
  821. ;=================== *calc_self_insert_button_widget_class* ===================
  822. ;==============================================================================
  823. (setq *calc_self_insert_button_widget_class*
  824.       (send Class :new
  825.         '(name-string
  826.           )
  827.         '()
  828.         XM_PUSH_BUTTON_WIDGET_CLASS))
  829.  
  830. ;; override XM_PUSH_BUTTON_WIDGET_CLASS instance initializer
  831. (send *calc_self_insert_button_widget_class* :answer :isnew '(name &rest widget_args)
  832.       '(
  833.     (setq name-string name)
  834.     (apply 'send-super
  835.            `(:isnew :managed "self-insert-button" 
  836.             ,@widget_args
  837.             :XMN_LABEL_STRING    ,name
  838.             :XMN_FOREGROUND        "Green"
  839.             :XMN_BACKGROUND        "IndianRed"
  840.             ))
  841.     (send-super :set_callback :xmn_activate_callback '()
  842.           `(
  843.         (send *calc_display* :insert_string ,name)
  844.         ))
  845.  
  846.     self                ;return self
  847.     ))
  848.  
  849. (send *calc_self_insert_button_widget_class* :answer :get_name_string '()
  850.       '(name-string)            ;return value
  851.       )
  852.  
  853. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  854. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  855. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  856. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  857. (setq top_w
  858.       (send TOP_LEVEL_SHELL_WIDGET_CLASS :new 
  859.         :XMN_TITLE             "Winterp: GraphCalc"
  860.         :XMN_ICON_NAME         "GraphCalc"
  861.         :XMN_KEYBOARD_FOCUS_POLICY    :EXPLICIT ;a hack to kludge up a forced focus on the display widget so cursor can be seen...
  862.         ))
  863.  
  864. (setq vpaned_w
  865.       (send XM_PANED_WINDOW_WIDGET_CLASS :new :managed
  866.         "vpane" top_w
  867.         ))
  868.  
  869. (setq graph_w
  870.       (send XM_GRAPH_WIDGET_CLASS :new :managed :scrolled
  871.         "dag" vpaned_w
  872.         :xmn_height            400 ;need to give a default height to start up -- can resize window to change
  873.         :xmn_arc_draw_mode        :position_fixed
  874.         :xmn_orientation        *default_graph_orientation*
  875.         :xmn_child_spacing        15
  876.         :xmn_sibling_spacing    10
  877.         :xmn_editable        nil ;setting to T means the buttons won't be pushable
  878.         :xmn_auto_layout_mode t    ;setting to NIL makes layout much faster, but if NIL,
  879.                     ;then all sorts of weird layout problems occur and calling
  880.                     ;:layout programmatically doesn't work (note that hitting the
  881.                     ;"ReLayout" button does work thus I suspect bugs in the event handling mechanism in XmGraph).
  882.         ))
  883.  
  884. ;;; set the size of the graph window pane -- note Motif's lamo use of hidden
  885. ;;; scroller->drawingarea->graph hierarchy when dealing with a scrolled widget (grrr).
  886. (send (send (send graph_w :parent) :parent) :set_values ;
  887.       :xmn_height 500
  888.       )
  889.  
  890. ;;;(send graph_w :set_values
  891. ;;;      :xmn_child_spacing    15
  892. ;;;      :xmn_sibling_spacing    10
  893. ;;;      )
  894.  
  895.  
  896. ;;;;;;;;;;;;;;;;;;;;;;;;;;;; controlpanel for graph_w ;;;;;;;;;;;;;;;;;;;;;;;;;;
  897. (setq graph_controlpanel_w
  898.       (send XM_ROW_COLUMN_WIDGET_CLASS :new :managed
  899.         "controlpanel" vpaned_w
  900.         :XMN_ORIENTATION            :HORIZONTAL
  901.         :XMN_PACKING                :PACK_TIGHT
  902.         :XMN_ADJUST_LAST            nil
  903.         :XMN_ENTRY_ALIGNMENT    :alignment_center
  904.         ))
  905.  
  906. (setq graph_editable_tbw
  907.       (send XM_TOGGLE_BUTTON_WIDGET_CLASS :new :managed
  908.         "Editable" graph_controlpanel_w
  909.         :XMN_SET nil
  910.         ))
  911. (send graph_editable_tbw :set_callback :xmn_value_changed_callback '(CALLBACK_SET)
  912.       '(
  913.     (send graph_w :set_values :xmn_editable CALLBACK_SET)
  914.     (send graph_destroy_selected_pbw :set_values :xmn_sensitive CALLBACK_SET)
  915.     ))
  916.  
  917. (setq graph_destroy_selected_pbw
  918.       (send XM_PUSH_BUTTON_WIDGET_CLASS :new :managed
  919.         "Clear Selected" graph_controlpanel_w
  920.         :xmn_sensitive nil        ;note the button is inactive unless :XMN_EDITABLE is true
  921.         ))
  922. (send graph_destroy_selected_pbw :set_callback :xmn_activate_callback '()
  923.       '(
  924.     (let* (
  925.            (saved-*BREAKENABLE* *BREAKENABLE*)
  926.            (array-sel-widgets   (send graph_w :get_selected_nodes))
  927.            (num-sel-widgets     (length array-sel-widgets))
  928.            )
  929.       (do 
  930.        ((i 0 (1+ i)))
  931.        ((eq i num-sel-widgets))
  932.        (setq *BREAKENABLE* nil)
  933.        (errset (send (aref array-sel-widgets i) :destroy)) ;trap errors incase widget has already been destroyed via :destroy on it's parent...
  934.        (setq *BREAKENABLE* saved-*BREAKENABLE*)
  935.        )
  936.       )))
  937.  
  938. (setq graph_clear_pbw
  939.       (send XM_PUSH_BUTTON_WIDGET_CLASS :new :managed
  940.         "Clear All" graph_controlpanel_w
  941.         ))
  942. (send graph_clear_pbw :set_callback :xmn_activate_callback '()
  943.       '(
  944.     (send graph_w :destroy_all_nodes)
  945.     ))
  946.  
  947. (setq graph_relayout_pbw
  948.       (send XM_PUSH_BUTTON_WIDGET_CLASS :new :managed
  949.         "Re-Layout" graph_controlpanel_w
  950.         ))
  951. (send graph_relayout_pbw :set_callback :xmn_activate_callback '()
  952.       '(
  953.     (send graph_w :layout)
  954.     ))
  955.  
  956. (setq graph_reorient_pbw
  957.       (send XM_PUSH_BUTTON_WIDGET_CLASS :new :managed
  958.         "Flip Layout Direction" graph_controlpanel_w
  959.         ))
  960. (send graph_reorient_pbw :set_callback :xmn_activate_callback '()
  961.       '(
  962.     (send graph_w :set_values :xmn_reorient t)
  963.  
  964.     (cond
  965.      ((eq *default_graph_orientation* :horizontal)
  966.       (setq *default_graph_orientation* :vertical)
  967.       )
  968.      ((eq *default_graph_orientation* :vertical)
  969.       (setq *default_graph_orientation* :horizontal)
  970.       )
  971.      )
  972.  
  973.     (let* ((array-widgets   (send graph_w :get_nodes))
  974.            (num-widgets     (length array-widgets)))
  975.       (do 
  976.        ((i 0 (1+ i)))
  977.        ((eq i num-widgets))
  978.        (send (aref array-widgets i) :reorient)
  979.        ))
  980.     ))
  981.  
  982. ;;;;;;;;;;;;;;;;;;;;;;;;;;;; calculator display ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  983. (setq *calc_display*
  984.       (send *calc_display_class* :new vpaned_w
  985.         ))
  986.  
  987. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; the keyboard ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  988. ;;
  989. ;; just for kicks, I'm making a graph widget manage a bunch of buttons that
  990. ;; could easily be done via XmForm -- I want to play w/ using the graph widget
  991. ;; as a "direct manipulation" manager for widgets....
  992. ;;
  993. (setq calc_keyboard_w
  994.       (send XM_GRAPH_WIDGET_CLASS :new :managed
  995.         "calc-keyboard" vpaned_w
  996.         :xmn_arc_draw_mode        :position_fixed
  997.         :xmn_editable        nil ;setting to T means the buttons won't be pushable
  998.         :xmn_auto_layout_mode    nil ;we're doing our own layout here...
  999.         :xmn_orientation        :horizontal
  1000.         ))
  1001.  
  1002. (let ((keyboard-interkey-spacing 5)
  1003.       (keyboard-x-width 0)
  1004.       (keyboard-y-height 0)
  1005.       )
  1006.  
  1007.   (let ((y-offset keyboard-interkey-spacing)
  1008.     (x-offset keyboard-interkey-spacing)
  1009.     last-w)
  1010.     (setq last-w
  1011.       (send *calc_self_insert_button_widget_class* :new "1" calc_keyboard_w
  1012.         :XMN_X x-offset
  1013.         :XMN_Y y-offset))
  1014.     (setq x-offset
  1015.       (+ x-offset keyboard-interkey-spacing (car (send last-w :get_values :xmn_width nil))))
  1016.     (do*
  1017.      ((keys-list '("2" "3" "4" "5" "6" "7" "8" "9" "0" "-" "+")
  1018.          (cdr keys-list)))
  1019.      ((null keys-list))
  1020.      (setq last-w
  1021.        (send *calc_self_insert_button_widget_class* :new (car keys-list) calc_keyboard_w
  1022.          :XMN_X x-offset
  1023.          :XMN_Y y-offset))
  1024.      (setq x-offset
  1025.        (+ x-offset keyboard-interkey-spacing (car (send last-w :get_values :xmn_width nil))))
  1026.      )
  1027.  
  1028.     (setq keyboard-x-width (max keyboard-x-width x-offset))
  1029.     (setq x-offset (+ keyboard-interkey-spacing (/ (car (send last-w :get_values :xmn_height nil)) 2)))
  1030.     (setq y-offset (+ y-offset keyboard-interkey-spacing (car (send last-w :get_values :xmn_height nil))))
  1031.  
  1032.     (do*
  1033.      ((keys-list '("Q" "W" "E" "R" "T" "Y" "U" "I" "O" "P" "[" "]" "(" ")")
  1034.          (cdr keys-list)))
  1035.      ((null keys-list))
  1036.      (setq last-w
  1037.        (send *calc_self_insert_button_widget_class* :new (car keys-list) calc_keyboard_w
  1038.          :XMN_X x-offset
  1039.          :XMN_Y y-offset))
  1040.      (setq x-offset
  1041.        (+ x-offset keyboard-interkey-spacing (car (send last-w :get_values :xmn_width nil))))
  1042.      )
  1043.  
  1044.     (setq keyboard-x-width (max keyboard-x-width x-offset))
  1045.     (setq x-offset (+ keyboard-interkey-spacing (car (send last-w :get_values :xmn_height nil))))
  1046.     (setq y-offset (+ y-offset keyboard-interkey-spacing (car (send last-w :get_values :xmn_height nil))))
  1047.  
  1048.     (do*
  1049.      ((keys-list '("A" "S" "D" "F" "G" "H" "J" "K" "L" ";" "'" "<ret>/==")
  1050.          (cdr keys-list)))
  1051.      ((null keys-list))
  1052.      (setq last-w
  1053.        (send *calc_self_insert_button_widget_class* :new (car keys-list) calc_keyboard_w
  1054.          :XMN_X x-offset
  1055.          :XMN_Y y-offset))
  1056.      (setq x-offset
  1057.        (+ x-offset keyboard-interkey-spacing (car (send last-w :get_values :xmn_width nil))))
  1058.      )
  1059.  
  1060.     (setq keyboard-x-width (max keyboard-x-width x-offset))
  1061.     (setq x-offset (+ keyboard-interkey-spacing (* 2 (car (send last-w :get_values :xmn_height nil)))))
  1062.     (setq y-offset (+ y-offset keyboard-interkey-spacing (car (send last-w :get_values :xmn_height nil))))
  1063.  
  1064.     (do*
  1065.      ((keys-list '("Z" "X" "C" "V" "B" "N" "M" "," "." "/")
  1066.          (cdr keys-list)))
  1067.      ((null keys-list))
  1068.      (setq last-w
  1069.        (send *calc_self_insert_button_widget_class* :new (car keys-list) calc_keyboard_w
  1070.          :XMN_X x-offset
  1071.          :XMN_Y y-offset))
  1072.      (setq x-offset
  1073.        (+ x-offset keyboard-interkey-spacing (car (send last-w :get_values :xmn_width nil))))
  1074.      )
  1075.  
  1076.     (setq keyboard-x-width (max keyboard-x-width x-offset))
  1077.     (setq y-offset (+ y-offset keyboard-interkey-spacing (car (send last-w :get_values :xmn_height nil))))
  1078.     
  1079.     (setq last-w
  1080.       (send *calc_self_insert_button_widget_class* :new " " calc_keyboard_w
  1081.         :XMN_X (- x-offset 200 keyboard-interkey-spacing)
  1082.         :XMN_Y y-offset
  1083.         :XMN_WIDTH 200
  1084.         ))
  1085.  
  1086.     (setq keyboard-y-height (+ y-offset keyboard-interkey-spacing (car (send last-w :get_values :xmn_height nil))))
  1087.     )
  1088.  
  1089. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  1090. ;;; edit keys
  1091. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  1092.   (setq edit_pad_w
  1093.     (send XM_ROW_COLUMN_WIDGET_CLASS :new :managed "edit-panel" calc_keyboard_w
  1094.           :XMN_ORIENTATION        :vertical
  1095.           :XMN_PACKING        :pack_tight
  1096.           :XMN_ADJUST_LAST         nil
  1097.           :XMN_ENTRY_ALIGNMENT    :alignment_center
  1098.           :XMN_X            keyboard-x-width
  1099.           :XMN_Y            0
  1100.           ))
  1101.  
  1102.   (send (send XM_PUSH_BUTTON_WIDGET_CLASS :new :managed "edit-button" edit_pad_w
  1103.           :XMN_LABEL_STRING "Backspace"
  1104.           :XMN_FOREGROUND "Yellow"
  1105.           :XMN_BACKGROUND "DimGrey"
  1106.           )
  1107.     :add_callback :xmn_activate_callback '()
  1108.     '(
  1109.       (send *calc_display* :backspace)
  1110.       ))
  1111.  
  1112.   (send (send XM_PUSH_BUTTON_WIDGET_CLASS :new :managed "edit-button" edit_pad_w
  1113.           :XMN_LABEL_STRING "Delete"
  1114.           :XMN_FOREGROUND "Yellow"
  1115.           :XMN_BACKGROUND "DimGrey"
  1116.           )
  1117.     :add_callback :xmn_activate_callback '()
  1118.     '(
  1119.       (send *calc_display* :delete)
  1120.       ))
  1121.  
  1122.   (send (send XM_PUSH_BUTTON_WIDGET_CLASS :new :managed "edit-button" edit_pad_w
  1123.           :XMN_LABEL_STRING "Clear"
  1124.           :XMN_FOREGROUND "Yellow"
  1125.           :XMN_BACKGROUND "DimGrey"
  1126.           )
  1127.     :add_callback :xmn_activate_callback '()
  1128.     '(
  1129.       (send *calc_display* :clear)
  1130.       ))
  1131.  
  1132.   (send (send XM_ARROW_BUTTON_WIDGET_CLASS :new :managed "edit-button" edit_pad_w
  1133.           :XMN_ARROW_DIRECTION :arrow_right
  1134.           :XMN_FOREGROUND "Yellow"
  1135.           :XMN_BACKGROUND "DimGrey"
  1136.           )
  1137.     :add_callback :xmn_activate_callback '()
  1138.     '(
  1139.       (send *calc_display* :forward-char)
  1140.       ))
  1141.  
  1142.   (send (send XM_ARROW_BUTTON_WIDGET_CLASS :new :managed "edit-button" edit_pad_w
  1143.           :XMN_ARROW_DIRECTION :arrow_left
  1144.           :XMN_FOREGROUND "Yellow"
  1145.           :XMN_BACKGROUND "DimGrey"
  1146.           )
  1147.     :add_callback :xmn_activate_callback '()
  1148.     '(
  1149.       (send *calc_display* :backward-char)
  1150.       ))
  1151.  
  1152. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  1153. ;;; Numberpad
  1154. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  1155.  
  1156.   (setq numpad_w
  1157.     (send XM_ROW_COLUMN_WIDGET_CLASS :new :managed "numbers" calc_keyboard_w
  1158.           :XMN_ORIENTATION        :vertical
  1159.           :XMN_PACKING        :pack_column
  1160.           :XMN_NUM_COLUMNS        4
  1161.           :XMN_ADJUST_LAST         nil
  1162.           :XMN_ENTRY_ALIGNMENT    :alignment_center
  1163.           :XMN_X            keyboard-x-width
  1164.           :XMN_Y            0
  1165.           ))
  1166.  
  1167.   (send *calc_self_insert_button_widget_class* :new "7" numpad_w)
  1168.   (send *calc_self_insert_button_widget_class* :new "4" numpad_w)
  1169.   (send *calc_self_insert_button_widget_class* :new "1" numpad_w)
  1170.   (send (send *calc_self_insert_button_widget_class* :new "+/-" numpad_w)
  1171.     :set_callback :xmn_activate_callback '() ;override normal self-insert callback
  1172.     '(
  1173.       (send *calc_display* :change_sign)
  1174.       ))
  1175.   (send *calc_self_insert_button_widget_class* :new "8" numpad_w)
  1176.   (send *calc_self_insert_button_widget_class* :new "5" numpad_w)
  1177.   (send *calc_self_insert_button_widget_class* :new "2" numpad_w)
  1178.   (send *calc_self_insert_button_widget_class* :new "0" numpad_w)
  1179.  
  1180.   (send *calc_self_insert_button_widget_class* :new "9" numpad_w)
  1181.   (send *calc_self_insert_button_widget_class* :new "6" numpad_w)
  1182.   (send *calc_self_insert_button_widget_class* :new "3" numpad_w)
  1183.   (send *calc_self_insert_button_widget_class* :new "." numpad_w)
  1184.  
  1185.   (send *calc_self_insert_button_widget_class* :new "(" numpad_w)
  1186.   (send *calc_self_insert_button_widget_class* :new ")" numpad_w)
  1187.   (send *calc_self_insert_button_widget_class* :new "[" numpad_w)
  1188.   (send *calc_self_insert_button_widget_class* :new "]" numpad_w)
  1189.  
  1190. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  1191. ;;; the function buttons
  1192. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  1193.   (setq funcpad_w
  1194.     (send XM_ROW_COLUMN_WIDGET_CLASS :new :managed "functions" calc_keyboard_w
  1195.           :XMN_ORIENTATION        :vertical
  1196.           :XMN_PACKING        :pack_column
  1197.           :XMN_NUM_COLUMNS        2
  1198.           :XMN_ADJUST_LAST         nil
  1199.           :XMN_ENTRY_ALIGNMENT    :alignment_center
  1200.           :XMN_X            keyboard-x-width
  1201.           :XMN_Y            0
  1202.           ))
  1203.  
  1204.   (send (send XM_PUSH_BUTTON_WIDGET_CLASS :new :managed "func-button" funcpad_w
  1205.           :XMN_LABEL_STRING " * "
  1206.           :XMN_FOREGROUND "blue"
  1207.           :XMN_BACKGROUND "lightgrey"
  1208.           )
  1209.     :add_callback :xmn_activate_callback '()
  1210.     '(
  1211.       (send *calc_display* :exec_binary_operator '*)
  1212.       ))
  1213.   (send (send XM_PUSH_BUTTON_WIDGET_CLASS :new :managed "func-button" funcpad_w
  1214.           :XMN_LABEL_STRING " / "
  1215.           :XMN_FOREGROUND "blue"
  1216.           :XMN_BACKGROUND "lightgrey"
  1217.           )
  1218.     :add_callback :xmn_activate_callback '()
  1219.     '(
  1220.       (send *calc_display* :exec_binary_operator '/)
  1221.       ))
  1222.   (send (send XM_PUSH_BUTTON_WIDGET_CLASS :new :managed "func-button" funcpad_w
  1223.           :XMN_LABEL_STRING " - "
  1224.           :XMN_FOREGROUND "blue"
  1225.           :XMN_BACKGROUND "lightgrey"
  1226.           )
  1227.     :add_callback :xmn_activate_callback '()
  1228.     '(
  1229.       (send *calc_display* :exec_binary_operator '-)
  1230.       ))
  1231.   (send (send XM_PUSH_BUTTON_WIDGET_CLASS :new :managed "func-button" funcpad_w
  1232.           :XMN_LABEL_STRING " + "
  1233.           :XMN_FOREGROUND "blue"
  1234.           :XMN_BACKGROUND "lightgrey"
  1235.           )
  1236.     :add_callback :xmn_activate_callback '()
  1237.     '(
  1238.       (send *calc_display* :exec_binary_operator '+)
  1239.       ))
  1240.   (send (send XM_PUSH_BUTTON_WIDGET_CLASS :new :managed "func-button" funcpad_w
  1241.           :XMN_LABEL_STRING " ^ "
  1242.           :XMN_FOREGROUND "blue"
  1243.           :XMN_BACKGROUND "lightgrey"
  1244.           )
  1245.     :add_callback :xmn_activate_callback '()
  1246.     '(
  1247.       (send *calc_display* :exec_binary_operator 'expt)
  1248.       ))
  1249.   (send (send XM_PUSH_BUTTON_WIDGET_CLASS :new :managed "func-button" funcpad_w
  1250.           :XMN_LABEL_STRING " MOD "
  1251.           :XMN_FOREGROUND "blue"
  1252.           :XMN_BACKGROUND "lightgrey"
  1253.           )
  1254.     :add_callback :xmn_activate_callback '()
  1255.     '(
  1256.       (send *calc_display* :exec_binary_operator 'mod)
  1257.       ))
  1258.   (send XM_PUSH_BUTTON_WIDGET_CLASS :new :managed "func-button" funcpad_w
  1259.     :XMN_LABEL_STRING " "
  1260.     :XMN_FOREGROUND "blue"
  1261.     :XMN_BACKGROUND "lightgrey"
  1262.     :XMN_MAPPED_WHEN_MANAGED nil    ;NOTE: button unmapped - use it to take up space...
  1263.     )
  1264.   (send (send XM_PUSH_BUTTON_WIDGET_CLASS :new :managed "func-button" funcpad_w
  1265.           :XMN_LABEL_STRING " == "
  1266.           :XMN_FOREGROUND "blue"
  1267.           :XMN_BACKGROUND "lightgrey"
  1268.           )
  1269.     :add_callback :xmn_activate_callback '()
  1270.     '(
  1271.       (send *calc_display* :exec_binary_operator nil)
  1272.       ))
  1273.  
  1274. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  1275.  
  1276.   (send top_w :realize)            ;create all the windows -- note that the code below doesn't get
  1277.                     ;the correct width/height values in laying out keyboard unless
  1278.                     ;the toplevel widget is realized...
  1279.                     ;We wouldnt need this kludge if the graph widget wasn't beign
  1280.                     ;used to layout the keyboard....
  1281.  
  1282.   (let (height)
  1283.     (send graph_controlpanel_w :get_values :xmn_height 'height)
  1284.     (send graph_controlpanel_w :set_values
  1285.       :xmn_maximum height
  1286.       :xmn_minimum height
  1287.       )
  1288.     )
  1289.   (let (height)
  1290.     (send (send *calc_display* :parent) :get_values :xmn_height 'height)
  1291.     (send (send *calc_display* :parent) :set_values
  1292.       :xmn_maximum height
  1293.       :xmn_minimum height
  1294.       )
  1295.     )
  1296.   (send calc_keyboard_w :set_values
  1297.     :xmn_maximum keyboard-y-height
  1298.     :xmn_minimum keyboard-y-height
  1299.     )
  1300.  
  1301.   (setq keyboard-x-width (+ keyboard-x-width (car (send edit_pad_w :get_values :xmn_width nil))))
  1302.   (send numpad_w :set_values :xmn_x keyboard-x-width)
  1303.   (setq keyboard-x-width (+ keyboard-x-width (car (send numpad_w :get_values :xmn_width nil))))
  1304.   (send funcpad_w :set_values :xmn_x keyboard-x-width)
  1305.   (setq keyboard-x-width (+ keyboard-x-width (car (send funcpad_w :get_values :xmn_width nil))))
  1306.  
  1307.   ;; kludge to make toplevel window come up right -- this wouldn't happen if
  1308.   ;; the graph widget wasn't being used to layout the keyboard...
  1309.   (send top_w :set_values
  1310.     :xmn_width (+ keyboard-x-width keyboard-interkey-spacing)
  1311.     :xmn_height (+ 
  1312.              (car (send graph_w :get_values :xmn_height nil))
  1313.              (car (send graph_controlpanel_w :get_values :xmn_height nil))
  1314.              (car (send *calc_display* :get_values :xmn_height nil))
  1315.              keyboard-y-height)
  1316.     )
  1317.   )
  1318.  
  1319. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; END OF PROGRAM ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  1320.